package com.mf.ergate.web.config;

import com.mf.ergate.api.monitor.model.dto.LoginLogCreateDto;
import com.mf.ergate.web.common.utils.MfParamUtils;
import com.mf.ergate.web.domain.enums.generic.AppTypeEnum;
import com.mf.ergate.web.security.MfUserDetails;
import com.mf.ergate.web.security.captcha.CaptchaAuthenticationFilter;
import com.mf.ergate.web.security.provider.DbAuthenticationProvider;
import com.mf.ergate.web.security.provider.DbUserDetailsService;
import com.mf.ergate.web.service.monitor.LoginLogService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.ProviderManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler;
import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.authentication.rememberme.TokenBasedRememberMeServices;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Arrays;

/**
 * Web安全配置
 * User: zhaoming
 * Date: 2018-05-28
 * To change this template use File | Settings | File Templates.
 */
@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    private final static String KEY = "rememberme.manage.mf.com";

    @Autowired
    private DbAuthenticationProvider dbAuthenticationProvider;
    @Autowired
    private DbUserDetailsService dbUserDetailsService;
    @Autowired
    private LoginLogService loginLogService;

    @Override
    public void configure(WebSecurity web) throws Exception {
        web.ignoring().antMatchers("/public/**", "/static/**", "/api/**");
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        // URL权限控制
        http.authorizeRequests().antMatchers("/", "/web/**").authenticated().anyRequest().permitAll();
        // 自定义登录
        http.formLogin()
                .loginPage("/login")
                .failureUrl("/login?error=true")
                .loginProcessingUrl("/j_spring_security_check")
                .permitAll();

        // 关闭csrf
        http.csrf().disable();
        // 自动登录
        http.rememberMe().tokenValiditySeconds(60 * 60 * 24 * 14).key(KEY).userDetailsService(dbUserDetailsService);
        // 自定义退出
        http.logout().logoutUrl("/logout").logoutSuccessUrl("/login").invalidateHttpSession(true);
        // 用户密码鉴权过滤器
        UsernamePasswordAuthenticationFilter filter = new UsernamePasswordAuthenticationFilter();
        filter.setPostOnly(true);
        filter.setFilterProcessesUrl("/j_spring_security_check");
        filter.setUsernameParameter("j_username");
        filter.setPasswordParameter("j_password");
        filter.setAuthenticationManager(authenticationManager());
        filter.setRememberMeServices(new TokenBasedRememberMeServices(KEY, dbUserDetailsService));
        filter.setAuthenticationFailureHandler(new SimpleUrlAuthenticationFailureHandler("/login?error=true"));
        filter.setAuthenticationSuccessHandler(new LoginAuthenticationSuccessHandler());
        // 过滤器替换
        http.addFilterAt(filter, UsernamePasswordAuthenticationFilter.class);
        //session失效后跳转到登录页面
        http.sessionManagement().invalidSessionUrl("/login");
        //只允许一个用户登录,如果同一个账户两次登录,那么第一个账户将被踢下线,跳转到登录页面
        http.sessionManagement().maximumSessions(1).expiredUrl("/login");

    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.authenticationProvider(dbAuthenticationProvider);
        auth.userDetailsService(dbUserDetailsService);
    }

    @Override
    protected AuthenticationManager authenticationManager() throws Exception {
        ProviderManager authenticationManager = new ProviderManager(Arrays.asList(dbAuthenticationProvider));
        //不擦除认证密码，擦除会导致TokenBasedRememberMeServices因为找不到Credentials再调用UserDetailsService而抛出UsernameNotFoundException
        authenticationManager.setEraseCredentialsAfterAuthentication(false);
        return authenticationManager;
    }

    class LoginAuthenticationSuccessHandler extends SavedRequestAwareAuthenticationSuccessHandler {
        @Override
        public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
            try {
                MfUserDetails userDetails = (MfUserDetails) authentication.getPrincipal();
                LoginLogCreateDto loginLogCreateDto = new LoginLogCreateDto();
                loginLogCreateDto.setAppTypeCode(AppTypeEnum.APP_MANAGE.getIndex());
                loginLogCreateDto.setAppTypeName(AppTypeEnum.APP_MANAGE.getName());
                loginLogCreateDto.setLoginIp(request.getRemoteAddr());
                loginLogCreateDto.setUserNo(userDetails.getUserNo());
                loginLogCreateDto.setUserAccount(userDetails.getUserAccount());
                loginLogCreateDto.setUserName(userDetails.getUsername());
                MfParamUtils.buildInsertEntity(loginLogCreateDto);
                loginLogService.insert(loginLogCreateDto);
            } catch (Exception e) {
                e.printStackTrace();
            }
            super.onAuthenticationSuccess(request, response, authentication);
        }
    }
}
